package services

import (
	"pegasus/connections"
	"pegasus/datatypes/protocol"
	"pegasus/utils"
	"sync"
	"time"

	"github.com/gorilla/websocket"
)

//Logined 登录的人物链接
type Logined struct {
	ID        int64
	LastHeart time.Time //这个只有心跳例程才会更新所以不用加锁
}

//HubService 链接管理
type HubService struct {
	utils.ILogger
	manager  *connections.HubManager
	readChan chan connections.HubMessage
	logined  map[int64]*Logined //当前登录人物对应的HUB ID
	rwLocker *sync.RWMutex

	protocol protocol.Protocol
}

//NewHubService 创建HubService
func NewHubService(il utils.ILogger, po protocol.Protocol, serverCache int, clientCache int) *HubService {
	readChan := make(chan connections.HubMessage, serverCache)
	srv := &HubService{
		ILogger:  il,
		manager:  connections.NewHubManager(il, readChan),
		readChan: readChan,
		logined:  make(map[int64]*Logined),
		rwLocker: &sync.RWMutex{},
		protocol: po, //协议支持
	}
	srv.manager.SetClientQueueCache(clientCache)
	go srv.heartBeat() //心跳检测
	go srv.read()      //从客户端读取数据
	return srv
}

//Add 添加1个人物 之后要加上1个USER只能创建1个链接 目前是1个角色只能创建1个链接
func (srv *HubService) Add(logined int64, conn *websocket.Conn) {
	srv.rwLocker.Lock()
	defer srv.rwLocker.Unlock()
	if v, ok := srv.logined[logined]; ok {
		srv.manager.Kickout(v.ID)
	}
	client := &Logined{ID: srv.manager.Add(conn), LastHeart: time.Now()}
	srv.logined[logined] = client
}

func (srv *HubService) read() {
	for {
		select {
		case message := <-srv.readChan:
			// msg, err := srv.protocol.Unmarshal(message)
			// if err != nil {
			// 	//关闭client
			// }
			// switch msg.(type) {
			// case protocol.Heart:
			// 	//发送到心跳检测例程
			// }
			srv.Trace(message)
		}
	}
}

var heartBeatTick = 60 * time.Second

func (srv *HubService) heartBeat() {
	ticker := time.NewTicker(heartBeatTick)
	defer func() {
		ticker.Stop()
	}()
	for {
		select {
		case <-ticker.C:
			clients := srv.copyLogined()
			now := time.Now()
			tmpClients := make([]*Logined, 0, len(clients))
			for _, v := range clients {
				if now.Sub(v.LastHeart) > heartBeatTick {
					tmpClients = append(tmpClients, v)
				}
			}
			for _, v := range tmpClients {
				srv.removeClient(v)
			}
		}
	}
}

func (srv *HubService) removeClient(ct *Logined) {
	srv.rwLocker.Lock()
	defer srv.rwLocker.Unlock()
	for k, v := range srv.logined {
		if ct == v {
			srv.manager.Kickout(v.ID)
			delete(srv.logined, k)
			return
		}
	}
}

func (srv *HubService) copyLogined() map[int64]*Logined {
	tmpLogined := make(map[int64]*Logined)
	srv.rwLocker.RLock()
	defer srv.rwLocker.RUnlock()
	for k, v := range srv.logined {
		tmpLogined[k] = v
	}
	return tmpLogined
}
